<?php

class DBMEntityHolder {
	/**
	 * Store the entities
	 *
	 * @var array
	 */
	private $entities = array();
	
	/**
 	 * The instance of this object
 	 *
 	 * @var Object
 	 */
 	static private $instance = null;
	
	/**
  	 * Singleton
  	 * @name getInstance
  	 * @access public
  	 * @return DBMEntityHolder
  	 */
  	static function getInstance(){
  		if(self::$instance == NULL){
  			self::$instance = new DBMEntityHolder();
  		}
  		return self::$instance;
  	}
  	
  	static function getEntityFromName($entityName){
  		DBMEntityHolder::getInstance()->getEntity($entityName);
  	}
	
	/**
	 * Create the entities from the configuration file
	 *
	 * @param string $path
	 */
	public function createEntitiesFromXML($path){
		// check if the path exists and is readable
 		if(!is_file($path) || !is_readable($path)){
 			throw new Exception("The configuration file (".$path.") for the database entities doesn't exists or could not be readed!");
		}

		// start the dom reader
		$dom = new DOMDocument();
		
		// load the data
		@$dom->load($path);
		
		// get the entities
		$entities = $dom->getElementsByTagName("entity");

		// search the entities
		for($i = 0; $i < $entities->length; $i++){
			// get the entity node
			$entityNode = $entities->item($i);
			
			// get the informations
			$table		= utf8_decode($entityNode->getAttribute("table")); 
			$class		= utf8_decode($entityNode->getAttribute("class"));
			$schema		= utf8_decode($entityNode->getAttribute("schema"));
			$alias			= utf8_decode($entityNode->getAttribute("alias"));
			
			// format the informations
			if($alias == "") { $alias = $table; }
			if($schema == "") { $schema = null; }

			// create the entity
			$entity = new DBMEntity($class, $table, $class, $schema, $alias);
			
			// get the attributes and relationships
			$primaryKeyNode 		= $entityNode->getElementsByTagName("primary-key")->item(0);
			$attributesNode 			= $entityNode->getElementsByTagName("attributes")->item(0);
			$relationshipsNode 	= $entityNode->getElementsByTagName("relationships")->item(0);
			
			// check if the primary key node exists
			if(!is_null($primaryKeyNode)){
				// create the primary key
				$primaryKey = new DBMPrimaryKey();
				
				// get the attributes
				$pkAttributesList = $primaryKeyNode->getElementsByTagName("attribute");
				
				// search the attributes
				for($c = 0; $c < $pkAttributesList->length; $c++){
					// get the attribute node
					$pkAttributeNode = $pkAttributesList->item($c);
					
					// converts the node to a new DBMAttribute object
					$attribute = $this->createAttributeFromNode($pkAttributeNode);
					
					// set the attribute as primary key
					$attribute->setPrimaryKey(true);
					
					// set the valid operations on the attribute
					$attribute->setUpdateable(false);
					
					// add the attribute
					$primaryKey->addAttribute($attribute);
				}
				
				// set the primary key on the entity
				$entity->setPrimaryKey($primaryKey);
			}

			// check if the attributes node exists
			if(!is_null($attributesNode)){
				// get the attributes
				$attributesList = $attributesNode->getElementsByTagName("attribute");
				
				// search the attributes
				for($c = 0; $c < $attributesList->length; $c++){
					// get the attribute node
					$attributeNode = $attributesList->item($c);
					
					// converts the node to a new DBMAttribute object
					$attribute = $this->createAttributeFromNode($attributeNode);
					
					// add the attribute
					$entity->addAttribute($attribute);
				}
			}			
			
			// check if the relationships node exists
			if(!is_null($relationshipsNode)){
				// get the one to one relationships
				$relationsOneToOneList 		= $relationshipsNode->getElementsByTagName("one-to-one");
				$relationsOneToManyList 	= $relationshipsNode->getElementsByTagName("one-to-many");
				
				// search the one-to-one relationships
				for($c = 0; $c < $relationsOneToOneList->length; $c++){
					// create the one-to-one relationship
					$relationship = $this->createRelationshipFromNode(DBMRelationship::TYPE_ONE_TO_ONE, $entity->getName(), $relationsOneToOneList->item($c));
					
					// add the relationship to the entity
					$entity->addRelationships($relationship);
				}
				
				// search the one-to-many relationships
				for($c = 0; $c < $relationsOneToManyList->length; $c++){
					// create the one-to-one relationship
					$relationship = $this->createRelationshipFromNode(DBMRelationship::TYPE_ONE_TO_MANY, $entity->getName(), $relationsOneToManyList->item($c));
					
					// add the relationship to the entity
					$entity->addRelationships($relationship);
				}
			}
			
			// store the entity
			$this->entities[$entity->getName()] = $entity;
		}
	}

	/**
	 * Return the entity specified by $name
	 *
	 * @param string $name
	 * @return DBMEntity The entity or null if the entity wasn't been found
	 */
	public function getEntity($name){
		if(isset($this->entities[$name])){
			return $this->entities[$name];
		} else {
			return null;
		}
	}

	/**
	 * Create a new DBMAttribute object based on the xml node (DOMNode object)
	 *
	 * @param DOMNode $node The source node
	 * @return DBMAttribute The created attribute
	 */
	private function createAttributeFromNode(DOMNode $node){
		// get the informations
		$name		= utf8_decode($node->getAttribute("name"));
		$column	= utf8_decode($node->getAttribute("column"));
		$alias		= utf8_decode($node->getAttribute("alias"));
		
		// format the informations
		if($column == "") { $column = null; }
		if($alias == "") { $alias = null; }
		
		// create the attribute
		$attribute = new DBMAttribute($name, $column);
		
		// check if the attribute is insertable, selectable, updateable and deletable
		if(utf8_decode($node->getAttribute("insertable")) == "false"){
			$attribute->setInsertable(false);
		}
		if(utf8_decode($node->getAttribute("selectable")) == "false"){
			$attribute->setSelectable(false);
		}
		if(utf8_decode($node->getAttribute("updateable")) == "false"){
			$attribute->setUpdateable(false);
		}
		if(utf8_decode($node->getAttribute("deletable")) == "false"){
			$attribute->setDeletable(false);
		}
		
		return $attribute;
	}
	
	/**
	 * Create a new DBMRelationship object based on the xml node (DOMNode object)
	 *
	 * @param DOMNode $node The source node
	 * @return DBMRelationship The created relationship
	 */
	private function createRelationshipFromNode($type, $entity, DOMNode $node){
		// get the informations
		$targetEntity 		= utf8_decode($node->getAttribute("target-entity"));
		$attribute			= utf8_decode($node->getAttribute("attribute"));
		$connectionsList	= $node->getElementsByTagName("connection");
		
		// format the informations
		if($targetEntity == "" || $attribute == "" || $connectionsList->length == 0) { 
			throw new IllegalRelationshipException("The relationship configuration of the entity " . $entity->getName() . " is invalid, please check your configuration XML!"); 
		}
		
		// create the relationship
		$relationship = new DBMRelationship();
		
		// set the relationship data
		$relationship->setSourceEntity($entity);
		$relationship->setTargetEntity($targetEntity);
		$relationship->setAttribute($attribute);
		$relationship->setType($type);
		
		// search the connections
		for($i = 0; $i < $connectionsList->length; $i++){
			// get the connection node
			$connectionNode = $connectionsList->item($i);
			
			// get the connection data
			$sourceAttribute	= $connectionNode->getAttribute("source-attribute");
			$targetAttribute	= $connectionNode->getAttribute("target-attribute");
			
			// check the data integrity
			if($sourceAttribute == "" || $targetAttribute == ""){
				continue;
			}
			
			// create the relationship connection
			$relationship->addConnection($sourceAttribute, $targetAttribute);
		}

		// return the relationship
		return $relationship;
	}
}

?>